/* EasyWay Game Engine
 * Copyright (C) 2006 Daniele Paggi.
 *  
 * Written by: 2006 Daniele Paggi<dshnt@hotmail.com>
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/**
 * package containing different utility.
 */
package org.easyway.utils;

import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import javax.imageio.ImageIO;

import org.easyway.interfaces.base.ITexture;
import org.easyway.objects.texture.TextureCompact;
import org.easyway.system.StaticRef;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.devil.IL;
import org.lwjgl.devil.ILU;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.glu.GLU;

/**
 * class to simplify images management.
 * 
 * 
 * @author Daniele Paggi
 * @version 1
 * @javadoc Linda Ferrari
 */
public class ImageUtils {

	public static boolean USE_MIPMAP = true;

	private ImageUtils() {
	}

	// ----------------------------------------------------------------------
	// ----------------------------LOAD TEXTURE------------------------------
	// ----------------------------------------------------------------------
	/** inits the utility */
	public static void init() {
		try {
			IL.create();
			ILU.create();
		} catch (LWJGLException e) {
			Utility.error("Error on creating/Initializing \"IL\" engine", e);
		}
	}

	/**
	 * loads a texture
	 * 
	 * @param path
	 *            file path
	 * @return the loaded texture
	 */
	public static TextureCompact loadTexture(String path) {
		return loadTexture(path, false, (byte) 0, (byte) 0, (byte) 0, false);
	}

	/**
	 * loads a texture and makes the given (RGB) colour transparent.
	 * 
	 * @param path
	 *            file path
	 * @param red
	 *            red component or RGB value
	 * @param green
	 *            green component of RGB value
	 * @param blue
	 *            blue component of RGB value
	 * @return the loaded texture
	 */
	public static TextureCompact loadTexture(String path, byte red, byte green,
			byte blue) {
		return loadTexture(path, true, red, green, blue, true);
	}

	/**
	 * loads a texture and makes the given (RGB) colour transparent. <br>
	 * if 'maskered' is 'true' will be used a technique that will improve the
	 * grafic aspect of the image in case it will be magnified.<br>
	 * For example if the texture is 128x128 pixel and is associated to a sprite
	 * of 512x512 pixels is advisable to set up maskered as 'true'.
	 * 
	 * @param path
	 *            file path
	 * 
	 * @param red
	 *            red component or RGB value
	 * @param green
	 *            green component of RGB value
	 * @param blue
	 *            blue component of RGB value
	 * @param maskered
	 *            if 'maskered' is 'true' will be used a technique that will
	 *            improve the grafic aspect of the image in case it will be
	 *            magnified.
	 * @return the loaded texture
	 */
	public static TextureCompact loadTexture(String path, byte red, byte green,
			byte blue, boolean maskered) {
		return loadTexture(path, true, red, green, blue, maskered);
	}

	/**
	 * loads a texture and, if 'makeTransp' is 'true', makes the given
	 * (RGB)colour transparent. <br>
	 * if 'maskered' is 'true' will be used a technique that will improve the
	 * grafic aspect of the image in case it will be magnified.<br>
	 * For example if the texture is 128x128 pixel and is associated to a sprite
	 * of 512x512 pixels is advisable to set up maskered as 'true'.
	 * 
	 * @param path
	 *            file path
	 * @param makeTrans
	 *            indicates if the image is to make transparent for the spefied
	 *            RGB
	 * @param red
	 *            red component or RGB value
	 * @param green
	 *            green component of RGB value
	 * @param blue
	 *            blue component of RGB value
	 * @param maskered
	 *            if 'maskered' is 'true' will be used a technique that will
	 *            improve the grafic aspect of the image in case it will be
	 *            magnified<==
	 * @return the loaded texture
	 */
	public static TextureCompact loadTexture(String path, boolean makeTransp,
			byte red, byte green, byte blue, boolean maskered) {
		if (!IL.isCreated())
			init();

		InputStream is = null;
		{

			if (path.startsWith("/"))
				path = path.substring(1);
			// while (path.indexOf("\\") != -1)
			// path = path.substring(0, path.indexOf("\\")) + "/"
			// + path.substring(path.indexOf("\\") + 1);
			path = path.replace("\\", "/");

			// String temp = "";// "./../../..";
			// if (path.charAt(0) != '/')
			// temp = temp + '/';
			try {
				is = Thread.currentThread().getContextClassLoader()
						.getResource(path).openStream();
			} catch (Exception e) {
				Utility.error("Image " + path + " was not found!", e);
			}

			// Utility.class.getResourceAsStream(temp + path);
		}

		Utility.error("Trying to load \"" + path + "\" image as Stream.",
				"ImageUtilis.loadTexture");
		return loadTexture(is, getType(path), makeTransp, red, green, blue,
				maskered);
	}

	/**
	 * loads a texture from Stream and, if 'makeTransp' is 'true', makes the
	 * given (RGB) colour transparent. <br>
	 * if 'maskered' is 'true' will be used a technique that will improve the
	 * grafic aspect of the image in case it will be magnified.<br>
	 * For example if the texture is 128x128 pixel and is associated to a sprite
	 * of 512x512 pixels is advisable to set up maskered as 'true' .<br>
	 * <br>
	 * Nota:<br>
	 * To know the type of image I advise you to use the getType method present
	 * in this class.<br>
	 * for example:<br>
	 * <b>loadTexture(</b> myInputStream<b>,</b> getType<b>("</b>BMP<b>"),</b>
	 * true<b>,</b> 255<b>,</b> 255<b>,</b> 0<b>,</b> true<b>);</b>
	 * 
	 * loads a texture
	 * 
	 * @see getType( String )
	 * 
	 * @param is
	 *            inputstream
	 * @param Type
	 *            type of image (I suggest you to retrive it using the
	 *            getType(String) function)
	 * @param makeTrans
	 *            indicates if the image is to make trasparent for the spefied
	 *            RGB
	 * @param red
	 *            red component or RGB value
	 * @param green
	 *            green component of RGB value
	 * @param blue
	 *            blue component of RGB value
	 * @param maskered
	 *            if 'maskered' is 'true' will be used a tecnique thet will
	 *            improve the grafic aspect of the image in case it will be
	 *            magnified.<==
	 * @return
	 */
	public static TextureCompact loadTexture(InputStream is, int Type,
			boolean makeTransp, byte red, byte green, byte blue,
			boolean maskered) {

		if (is == null) {
			Utility.error("InputStream is NULL", "Utility.loadTexture");
			return null;
		}
		// TODO: getType
		try {
			IL.ilLoadFromStream(is, Type);
		} catch (Exception e) {
			Utility.error("Error to load Image", "Utility.loadTexture", e);
		}
		// &&&&&&&&&&
		int type = GL11.GL_RGBA;// GL11.GL_RGB;
		// if (isRGBA)
		// type = GL11.GL_RGBA;
		// &&&&&&&&&&

		IL.ilConvertImage(type, IL.IL_BYTE);
		if (Type == IL.IL_BMP) {
			ILU.iluFlipImage();
		}

		int width = IL.ilGetInteger(IL.IL_IMAGE_WIDTH);
		int height = IL.ilGetInteger(IL.IL_IMAGE_HEIGHT);
		int textureWidthSize = getNextPowerOfTwo(width);
		int textureHeightSize = getNextPowerOfTwo(height);
		ByteBuffer scratch = null;

		TextureCompact texture = new TextureCompact();
		texture.widthHW = textureWidthSize;
		texture.heightHW = textureHeightSize;
		texture.height = height;
		texture.width = width;
		texture.isRegular = true;

		if (textureWidthSize != width || textureHeightSize != height) {
			Utility.error(
					"the image hasn't standard dimension: 2, 4, 8, 16, 32..",
					"ImageUtils.loadTexture(InputStream)");
			texture.isRegular = false;
			scratch = BufferUtils.createByteBuffer(textureWidthSize
					* textureHeightSize * 4);
			IL.ilCopyPixels(0, 0, 0, textureWidthSize, textureHeightSize, 1,
					IL.IL_RGBA, IL.IL_BYTE, scratch);
		} else {
			scratch = IL.ilGetData();
		}

		// alloca lo spazio per la texture
		IntBuffer buf = BufferUtils.createIntBuffer(4);
		GL11.glGenTextures(buf);
		texture.texture = buf.get(0); // ID
		texture.textureBuffer = buf;
		texture.isSolid = !makeTransp;
		texture.bind();
		// GL11.glBindTexture(GL11.GL_TEXTURE_2D, buf.get(0));


		if (makeTransp) {
			scratch.rewind();
			byte r, g, b;
			long tot = textureWidthSize * textureHeightSize;
			for (long i = 0; i < tot; ++i) {
				scratch.mark();
				r = scratch.get();
				g = scratch.get();
				b = scratch.get();

				if (r == red && g == green && b == blue) {
					scratch.reset();
					scratch.put((byte) 0);
					scratch.put((byte) 0);
					scratch.put((byte) 0);
					scratch.put((byte) 0);
				} else
					scratch.get();
			}
			scratch.rewind();
		}

		// Filtering
		if (USE_MIPMAP) {
			GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
					GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
		} else {
			GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
					GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
		}
		// GL11.GL_LINEAR);
		// GL11.GL_NEAREST);

		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
				GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S,
				GL12.GL_CLAMP_TO_EDGE);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T,
				GL12.GL_CLAMP_TO_EDGE);

		// Generate The Texture - NEW
		if (USE_MIPMAP) {
			GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA,
					texture.widthHW, texture.heightHW, GL11.GL_RGBA,
					GL11.GL_UNSIGNED_BYTE, scratch);
		} else {
			GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA,
					texture.widthHW, texture.heightHW, 0, GL11.GL_RGBA,
					GL11.GL_UNSIGNED_BYTE, scratch);
		}
		Utility.error("End loading \"" + is + "\" image as Stream.",
				"ImageUtilis.loadTexture");
		IL.ilDeleteImages(scratch.asIntBuffer());

		texture.calculateUV();
		// texture.depure(0.5f);
		if (maskered && makeTransp)
			texture.alphaMask = createMask(texture);
		return texture;
	}

	/**
	 * Makes a new Texture with the given sizes
	 * 
	 * @param width
	 *            the width of image
	 * @param height
	 *            the height of image
	 * @return the maked texture
	 */
	public static TextureCompact makeTexture(int width, int height) {
		int textureWidthSize = getNextPowerOfTwo(width);
		int textureHeightSize = getNextPowerOfTwo(height);
		ByteBuffer scratch = null;

		TextureCompact texture = new TextureCompact();
		texture.widthHW = textureWidthSize;
		texture.heightHW = textureHeightSize;
		texture.height = height;
		texture.width = width;
		texture.isSolid = true;

		if (textureWidthSize != width || textureHeightSize != height) {
			Utility.error(
					"the image hasn't standard dimension: 2, 4, 8, 16, 32..",
					"ImageUtilis.makeTexture");
			texture.isRegular = false;
		} else {
			texture.isRegular = true;
		}
		scratch = BufferUtils.createByteBuffer(textureWidthSize
				* textureHeightSize * 4);

		// alloca lo spazio per la texture
		IntBuffer buf = BufferUtils.createIntBuffer(4);
		GL11.glGenTextures(buf);
		texture.texture = buf.get(0); // ID
		texture.textureBuffer = buf;
		texture.bind();
		// Filtering
		// GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
		// GL11.GL_LINEAR_MIPMAP_LINEAR);
		// GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
		// GL11.GL_LINEAR);
		// GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S,
		// GL12.GL_CLAMP_TO_EDGE);
		// GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T,
		// GL12.GL_CLAMP_TO_EDGE);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
				GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
				GL11.GL_LINEAR);

		// Generate The Texture - NEW
		// GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA,
		// texture.widthHW, texture.heightHW, GL11.GL_RGBA,
		// GL11.GL_UNSIGNED_BYTE, scratch);

		// Generate The Texture - old
		// GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, type, texture.widthHW,
		// texture.heightHW, 0, type, GL11.GL_UNSIGNED_BYTE, scratch);
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, texture.widthHW,
				texture.heightHW, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE,
				scratch);
		Utility.error("End making empty image of sizes: " + width + " "
				+ height, "ImageUtilis.makeTexture");
		// IL.ilDeleteImages(scratch.asIntBuffer());
		texture.calculateUV();
		return texture;
	}

	/**
	 * creates collision mask from the texture.
	 * 
	 * @param texture
	 *            source texture
	 * @return a collision mask generated from the source texture.
	 */
	public static TextureCompact createMask(TextureCompact texture) {
		ByteBuffer data = texture.getData();

		TextureCompact mtexture = new TextureCompact(texture);
		mtexture.isSolid = true;

		IntBuffer buf = BufferUtils.createIntBuffer(4);
		GL11.glGenTextures(buf);
		buf.rewind();
		mtexture.texture = buf.get(0);
		mtexture.textureBuffer = buf;
		// GL11.glBindTexture(GL11.GL_TEXTURE_2D, buf.get(0));
		mtexture.bind();

		data.rewind();
		long tot = texture.widthHW * texture.heightHW;
		for (long i = 0; i < tot; ++i) {
			data.mark();
			data.get();
			data.get();
			data.get();

			if (data.get() == (byte) 0) { // trasparente?
				data.reset();
				data.put((byte) 255);
				data.put((byte) 255);
				data.put((byte) 255);
				data.put((byte) 255);
			} else {
				data.reset();
				data.put((byte) 0);
				data.put((byte) 0);
				data.put((byte) 0);
				data.put((byte) 0);
			}
		}

		data.rewind();
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
				GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
				GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S,
				GL12.GL_CLAMP_TO_EDGE);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T,
				GL12.GL_CLAMP_TO_EDGE);
		GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA,
				texture.widthHW, texture.heightHW, GL11.GL_RGBA,
				GL11.GL_UNSIGNED_BYTE, data);

		return mtexture;
	}

	/**
	 * removes mistakes which are present on some ruined gif or png .<br>
	 * or problems you may have during the application of the blender-effect on
	 * images with transparents parts.
	 * 
	 * @param texture
	 *            source texture
	 * @param limit
	 *            a value [in range of 0:1] that indicates the depure
	 */
	public static void depureTexture(TextureCompact texture, float limit) {
		ByteBuffer data = texture.getData();
		data.rewind();
		// for (int i=0; i<256;i++)
		// System.out.println("i"+i+" "+(byte)i);
		byte alpha;
		int ialpha;
		int ilimit = (int) (limit * 255);
		long tot = texture.widthHW * texture.heightHW;
		for (int i = 0; i < tot; ++i) {
			data.mark();
			data.get();
			data.get();
			data.get();
			alpha = data.get();
			if (alpha < 0)
				ialpha = 256 + alpha;
			else
				ialpha = (int) alpha;
			if (ialpha < ilimit) { // trasparente?
				data.reset();
				data.put((byte) 0);
				data.put((byte) 0);
				data.put((byte) 0);
				data.put((byte) 0);
			}
		}
		texture.setData(data);
	}

	/**
	 * returns the type of a file from his extension
	 * 
	 * @param extension
	 *            extension of ile
	 * @return the file Type
	 */
	public static int getType(String extension) {
		// Save having to use equalsIgnoreCase all the time
		extension = extension.substring(extension.lastIndexOf('.') + 1)
				.toLowerCase();
		int type = IL.IL_TYPE_UNKNOWN;

		if (extension.equals("bmp")) {
			type = IL.IL_BMP;
		} else if (extension.equals("cut")) {
			type = IL.IL_CUT;
		} else if (extension.equals("gif")) {
			type = IL.IL_GIF;
		} else if (extension.equals("ico")) {
			type = IL.IL_ICO;
		} else if (extension.equals("jpg")) {
			type = IL.IL_JPG;
		} else if (extension.equals("lif")) {
			type = IL.IL_LIF;
		} else if (extension.equals("mng")) {
			type = IL.IL_MNG;
		} else if (extension.equals("pcd")) {
			type = IL.IL_PCD;
		} else if (extension.equals("pcx")) {
			type = IL.IL_PCX;
		} else if (extension.equals("pic")) {
			type = IL.IL_PIC;
		} else if (extension.equals("png")) {
			type = IL.IL_PNG;
		} else if (extension.equals("pbm") || extension.equals("pgm")
				|| extension.equals("ppm")) {
			type = IL.IL_PNM;
		} else if (extension.equals("psd")) {
			type = IL.IL_PSD;
		} else if (extension.equals("psp")) {
			type = IL.IL_PSP;
		} else if (extension.equals("bw") || extension.equals("rgb")
				|| extension.equals("rgba") || extension.equals("sgi")) {
			type = IL.IL_SGI;
		} else if (extension.equals("tga")) {
			type = IL.IL_TGA;
		} else if (extension.equals("tif") || extension.equals("tiff")) {
			type = IL.IL_TIF;
		} else if (extension.equals("dds")) {
			type = IL.IL_DDS;
		} else if (extension.equals("raw")) {
			type = IL.IL_RAW;
		} else if (extension.equals("lbm")) {
			type = IL.IL_LBM;
		} else if (extension.equals("jng")) {
			type = IL.IL_JNG;
		} else if (extension.equals("wal")) {
			type = IL.IL_WAL;
		} else if (extension.equals("pix")) {
			type = IL.IL_PIX;
		} else if (extension.equals("mdl")) {
			type = IL.IL_MDL;
		} else if (extension.equals("exif")) {
			type = IL.IL_EXIF;
		} else if (extension.equals("oil")) {
			type = IL.IL_OIL;
		} else if (extension.equals("dcx")) {
			type = IL.IL_DCX;
		} else if (extension.equals("pxr")) {
			type = IL.IL_PXR;
		} else if (extension.equals("xpm")) {
			type = IL.IL_XPM;
		} else if (extension.equals("pal")) {
			type = IL.IL_JASC_PAL;
		} else if (extension.equals("h")) {
			type = IL.IL_CHEAD;
		}

		return type;
	}

	/**
	 * Get the closest greater power of 2 to the fold number
	 * 
	 * @param fold
	 *            The target number
	 * @return The power of 2
	 */
	public static int getNextPowerOfTwo(int fold) {
		int pow = 2;
		while (pow < fold) {
			pow += pow;
		}
		return pow;
	}

	/**
	 * Generates an array of texture created by the source texture .<br>
	 * the generated texture have sizew and sizeh dimensions.
	 * 
	 * @param source
	 *            the source texture
	 * @param sizew
	 *            the width of generated texture
	 * @param sizeh
	 *            the height of generated texture
	 * @return an array of textures
	 */
	public static TextureCompact[] split1D(TextureCompact source, int sizew, int sizeh) {
		int nx = source.width / sizew;
		int ny = source.height / sizeh;
		TextureCompact split[] = new TextureCompact[nx * ny];
		ByteBuffer data = source.getData();
		ByteBuffer tdata;
		TextureCompact texture;

		byte r, g, b, a;
		int xx, yy;
		for (int y = 0; y < ny; y++) {
			for (int x = 0; x < nx; x++) {
				data.rewind();
				split[x + y * nx] = texture = new TextureCompact();
				texture.height = sizeh;
				texture.width = sizew;
				int hsizew = getNextPowerOfTwo(sizew);
				int hsizeh = getNextPowerOfTwo(sizeh);
				if (sizew == hsizew && sizeh == hsizeh) {
					texture.isRegular = true;
				} else {
					texture.isRegular = false;
				}
				texture.widthHW = hsizew;
				texture.heightHW = hsizeh;
				tdata = BufferUtils.createByteBuffer(hsizew * hsizeh * 4);
				tdata.rewind();
				xx = 0;
				yy = 0;
				int cx = 0;
				int cy = 0;
				// while (data.remaining() > 0) {
				for (yy = 0; yy < source.heightHW; yy++) {
					if (yy >= source.height)
						continue;
					for (xx = 0; xx < source.widthHW; xx++) {
						r = data.get();
						g = data.get();
						b = data.get();
						a = data.get();
						if (xx >= source.width)
							continue;
						if (((int) (xx / sizew) == x)
								&& ((int) (yy / sizeh) == y)) {
							tdata.put(r);
							tdata.put(g);
							tdata.put(b);
							tdata.put(a);
							cx++;
							if (cx >= sizew) {
								cy++;
								while (cx < hsizew) {
									tdata.put((byte) 0);
									tdata.put((byte) 0);
									tdata.put((byte) 0);
									tdata.put((byte) 0);
									cx++;
								}
								cx = 0;
							}
						}
					}
				}
				tdata.rewind();
				IntBuffer buf = BufferUtils.createIntBuffer(4);
				GL11.glGenTextures(buf);
				GL11.glBindTexture(GL11.GL_TEXTURE_2D, buf.get(0));
				texture.texture = buf.get(0); // ID
				texture.textureBuffer = buf;
				GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
						GL11.GL_TEXTURE_MIN_FILTER,
						GL11.GL_LINEAR_MIPMAP_LINEAR);
				GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
						GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); // Generate

				GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
						GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
				GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
						GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
				// The
				// Texture
				GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA,
						texture.widthHW, texture.heightHW, GL11.GL_RGBA,
						GL11.GL_UNSIGNED_BYTE, tdata);

				// GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA,
				// texture.widthHW, texture.heightHW, 0, GL11.GL_RGBA,
				// GL11.GL_UNSIGNED_BYTE, tdata);
				// IL.ilDeleteImages(tdata.asIntBuffer());

				texture.calculateUV();
			}
		}

		return split;
	}

	/**
	 * Generates a bidimensional array of texture created by the source texture.<br>
	 * The generated texture have sizew e sizeh dimensions.
	 * 
	 * @param source
	 *            the source texture
	 * @param sizew
	 *            the width of generated texture
	 * @param sizeh
	 *            the height of generated texture
	 * @return an array of textures
	 */
	public static TextureCompact[][] split2D(TextureCompact source, int sizew, int sizeh) {
		int nx = source.width / sizew;
		int ny = source.height / sizeh;
		TextureCompact split[][] = new TextureCompact[nx][ny];
		ByteBuffer data = source.getData();
		ByteBuffer tdata;
		TextureCompact texture;

		byte r, g, b, a;
		int xx, yy;
		for (int y = 0; y < ny; y++) {
			for (int x = 0; x < nx; x++) {
				data.rewind();

				split[x][y] = texture = new TextureCompact();
				texture.height = sizeh;
				texture.width = sizew;
				int hsizew = getNextPowerOfTwo(sizew);
				int hsizeh = getNextPowerOfTwo(sizeh);

				if (sizew == hsizew && sizeh == hsizeh) {
					texture.isRegular = true;
				} else {
					texture.isRegular = false;
				}
				texture.widthHW = hsizew;
				texture.heightHW = hsizeh;

				tdata = BufferUtils.createByteBuffer(hsizew * hsizeh * 4);
				tdata.rewind();
				xx = 0;
				yy = 0;
				int cx = 0;
				int cy = 0;
				// while (data.remaining() > 0) {
				for (yy = 0; yy < source.heightHW; yy++) {
					if (yy >= source.height)
						continue;
					for (xx = 0; xx < source.widthHW; xx++) {
						r = data.get();
						g = data.get();
						b = data.get();
						a = data.get();
						if (xx >= source.width)
							continue;
						if (((int) (xx / sizew) == x)
								&& ((int) (yy / sizeh) == y)) {
							tdata.put(r);
							tdata.put(g);
							tdata.put(b);
							tdata.put(a);
							cx++;
							if (cx >= sizew) {
								cy++;
								while (cx < hsizew) {
									tdata.put((byte) 0);
									tdata.put((byte) 0);
									tdata.put((byte) 0);
									tdata.put((byte) 0);
									cx++;
								}
								cx = 0;
							}
						}

					}
				}
				tdata.rewind();
				IntBuffer buf = BufferUtils.createIntBuffer(4);
				GL11.glGenTextures(buf);
				GL11.glBindTexture(GL11.GL_TEXTURE_2D, buf.get(0));
				texture.texture = buf.get(0); // ID
				texture.textureBuffer = buf;
				GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
						GL11.GL_TEXTURE_MIN_FILTER,
						GL11.GL_LINEAR_MIPMAP_LINEAR);
				GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
						GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
				GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
						GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
				GL11.glTexParameteri(GL11.GL_TEXTURE_2D,
						GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
				// Generate The Texture
				GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA,
						texture.widthHW, texture.heightHW, GL11.GL_RGBA,
						GL11.GL_UNSIGNED_BYTE, tdata);

				// GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA,
				// texture.widthHW, texture.heightHW, 0, GL11.GL_RGBA,
				// GL11.GL_UNSIGNED_BYTE, tdata);
				// IL.ilDeleteImages(tdata.asIntBuffer());

				texture.calculateUV();
			}
		}

		return split;
	}

	public static TextureCompact getScreenShot() {
		TextureCompact texture = new TextureCompact();
		texture.widthHW = 1024;
		texture.width = StaticRef.getCamera().getWidth();
		texture.heightHW = 1024;
		texture.height = StaticRef.getCamera().getHeight();
		texture.calculateUV();
		texture.isRegular = false;
		texture.isSolid = true;

		IntBuffer point = BufferUtils.createIntBuffer(4);
		GL11.glGenTextures(point);
		texture.texture = point.get(0);
		texture.textureBuffer = point;
		texture.bind();

		point.rewind();
		// GL11.glBindTexture(GL11.GL_TEXTURE_2D, point.get(0));
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
				GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
				GL11.GL_LINEAR);
		GL11.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, 0, 0,
				texture.widthHW, texture.heightHW, 0);
		return texture;
	}

	/**
	 * saves a texture on a png file
	 * 
	 * @param texture
	 *            the texture to save
	 * @param path
	 *            the file path
	 */
	public static void savePngImage(TextureCompact texture, String path) {
		saveImage(texture, path, "png");
	}

	/**
	 * saves a texture on a jpg file
	 * 
	 * @param texture
	 *            the texture to save
	 * @param path
	 *            the file path
	 */
	public static void saveJpgImage(TextureCompact texture, String path) {
		saveImage(texture, path, "jpg");
	}

	/**
	 * saves a texture on a bmp file
	 * 
	 * @param texture
	 *            the texture to save
	 * @param path
	 *            the file path
	 */
	public static void saveBmpImage(TextureCompact texture, String path) {
		saveImage(texture, path, "bmp");
	}

	/**
	 * saves a texture on a image file<br>
	 * example: ImageUtils.saveImage( myTexture, "C:\image.png", "png" );
	 * 
	 * @param texture
	 *            the texture to save
	 * @param path
	 *            the file path
	 * @param format
	 *            the type of image, for example: "bmp"
	 */
	public static void saveImage(TextureCompact texture, String path, String format) {
		BufferedImage image = new BufferedImage(texture.width, texture.height,
				BufferedImage.TYPE_INT_RGB);
		WritableRaster raster = image.getRaster();

		ByteBuffer bb = texture.getData();
		bb.rewind();
		for (int y = 0; y < texture.heightHW; ++y) {
			if (y >= texture.height)
				break;
			for (int x = 0; x < texture.widthHW; ++x) {
				if (x >= texture.width) {
					bb.get(); // r
					bb.get(); // g
					bb.get(); // b
					bb.get(); // a
					continue;
				}
				raster.setPixel(x, texture.height - 1 - y, new int[] {
						bb.get(), bb.get(), bb.get() });
				bb.get(); // a
			}
		}
		try {
			ImageIO.write(image, "png", new File(path));
		} catch (IOException e) {
			Utility.error("can't save the image on file " + path,
					"ImageUtils.saveImage", e);
		}
	}
	
	
	public static void freeSpace() {
		for (ITexture t : StaticRef.textures) {
			t.setData(null); // free memory
		}
	}
}
